package helper

import (
	"errors"
	"fmt"
	"reflect"
)

//结构体字段复制
func StructFieldCopy(src, dst interface{}) (err error) {
	// 防止意外panic
	defer func() {
		if er := recover(); er != nil {
			err = errors.New(fmt.Sprintf("%v", er))
		}
	}()

	srcType, srcValue := reflect.TypeOf(src), reflect.ValueOf(src)
	// src必须为结构体或者结构体指针
	if srcType.Kind() == reflect.Ptr {
		srcType, srcValue = srcType.Elem(), srcValue.Elem()
	}
	if srcType.Kind() != reflect.Struct {
		return errors.New("源对象必须是一个结构体对象或者结构体指针对象")
	}

	dstType, dstValue := reflect.TypeOf(dst), reflect.ValueOf(dst)
	// dst必须结构体指针类型
	if dstType.Kind() != reflect.Ptr || dstType.Elem().Kind() != reflect.Struct {
		return errors.New("目标对象必须是一个结构体指针对象")
	}

	// 取具体内容
	dstType, dstValue = dstType.Elem(), dstValue.Elem()

	// 目标对象属性个数
	dstFieldNums := dstType.NumField()
	for i := 0; i < dstFieldNums; i++ {
		// 属性
		dstField := dstType.Field(i)
		// 待填充属性值
		srcFieldValue := srcValue.FieldByName(dstField.Name)

		// 无效，说明src没有这个属性 || 属性同名但类型不同
		if !srcFieldValue.IsValid() || dstField.Type != srcFieldValue.Type() {
			continue
		}

		if dstValue.Field(i).CanSet() {
			dstValue.Field(i).Set(srcFieldValue)
		}
	}
	return nil
}

//把结构体转为map对象
func StructToMap(src interface{}) (map[string]string, error) {
	v := reflect.ValueOf(src)
	if v.Type().Kind() == reflect.Ptr {
		v = v.Elem()
	}
	if v.Kind() != reflect.Struct {
		return nil, errors.New("源对象必须是一个结构体对象或者结构体指针对象")
	}

	data := make(map[string]string)
	srcType := v.Type()

	for i := 0; i < v.NumField(); i++ {
		field := v.Field(i)
		tField := srcType.Field(i)
		data[tField.Name] = field.String()
	}
	return data, nil
}
